use std::sync::Arc;

use tokio::sync::Mutex;
use tracing::{error, info};

use bc_base::{
    BlockId, TransactionId,
    constants::{COINBASE_TX_ID_OF_GENESIS_BLOCK, GENESIS_BLOCK_ID},
};

use crate::top_storage::{
    BlockAndIndexStorageWithCache, MiscItemStorageWithCache, TransactionAndIndexStorageWithCache,
};

pub mod block;
pub mod mining;
pub mod network;
pub mod top_storage;
pub mod transaction;

//
// mini-blockchain has multiple databases, each database corresponds to a folder,
// the folders are in the same parent folder
//
pub const DEFAULT_BC_DB_ROOT_DIR: &str = "./mini-bc-db-folder";

pub async fn check_genesis_info_in_bc_storage(
    block_and_index_storage_with_cache: Arc<Mutex<BlockAndIndexStorageWithCache>>,
    tx_and_index_storage_with_cache: Arc<Mutex<TransactionAndIndexStorageWithCache>>,
    misc_item_storage_with_cache: Arc<Mutex<MiscItemStorageWithCache>>,
) {
    let genesis_block_id_from_str = BlockId::from_hex_str(GENESIS_BLOCK_ID).unwrap();
    let coinbase_tx_id_of_genesis_block_from_str =
        TransactionId::from_hex_str(COINBASE_TX_ID_OF_GENESIS_BLOCK).unwrap();

    let mut block_db = block_and_index_storage_with_cache.lock().await;
    let mut tx_db = tx_and_index_storage_with_cache.lock().await;
    if !block_db
        .block_index_exists(&genesis_block_id_from_str)
        .await
        || !tx_db
            .tx_exists(&coinbase_tx_id_of_genesis_block_from_str)
            .await
    {
        let (genesis_block_id, genesis_block, coinbase_tx) = block::create_genesis_block();
        assert_eq!(genesis_block_id, genesis_block_id_from_str);
        let tx_id = coinbase_tx.calc_tx_id();
        assert_eq!(tx_id, coinbase_tx_id_of_genesis_block_from_str);

        if let Err(e) = block_db
            .write_block_and_index_sync(&genesis_block_id, &genesis_block)
            .await
        {
            error!("Error storing genesis block: {:?}", e);
        } else {
            info!("Genesis block({}) stored successfully.", genesis_block_id);
        }
        if let Err(e) = tx_db
            .write_tx_and_index_sync(&tx_id, &coinbase_tx, Some(genesis_block_id))
            .await
        {
            error!("Error storing genesis coinbase transaction: {:?}", e);
        } else {
            info!(
                "Genesis coinbase transaction({}) stored successfully.",
                tx_id
            );
        }

        let mut misc_item_db = misc_item_storage_with_cache.lock().await;
        if let Err(e) = misc_item_db
            .update_misc_storage_for_genesis_block(&genesis_block_id, &genesis_block)
            .await
        {
            error!("Error updating misc storage for genesis block: {:?}", e);
        } else {
            info!("Misc storage updated successfully.");
        }

        block_db.flush_block_and_index().await.unwrap();
        tx_db.flush_tx_and_index().await.unwrap();
        misc_item_db.flush_database().await.unwrap();
    }
}
